#include "ServerSocket.h"
#include <cstdlib>

ServerSocket::ServerSocket(const std::string& port)
{
	this->port = port;

    struct addrinfo hints;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;
	int addressResult = getaddrinfo(NULL, port.c_str(), &hints, &addInfoResult);
    if (addressResult != 0)
    {
		Log::getInstance()->error("[SOCKET] getaddrinfo() failed! ; (ErrorCode: " + Log::getInstance()->intToString(addressResult) + " )");
    }

    socketConnection = socket(addInfoResult->ai_family, addInfoResult->ai_socktype, addInfoResult->ai_protocol);
    if (socketConnection == INVALID_SOCKET)
    {
		Log::getInstance()->error("[SOCKET] socket() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
    }
}

ServerSocket::~ServerSocket()
{
}

void ServerSocket::doBind()
{
	if (socketConnection == INVALID_SOCKET)
	{
		Log::getInstance()->error("[SOCKET] Unable to bind on invalid socket.");
		throw NetworkException("");
	}
	sockaddr_in localAddress;
	memset(&(localAddress.sin_zero), 0, 8);
	localAddress.sin_family = AF_INET ;
	localAddress.sin_port = htons(atoi(port.c_str()));
	localAddress.sin_addr.s_addr = INADDR_ANY ;
	
	int bindResult = bind(socketConnection, (sockaddr*)&localAddress, sizeof(localAddress));
    if (bindResult == SOCKET_ERROR)
    {
		Log::getInstance()->error("[SOCKET] bind() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
        throw NetworkException(WSAGetLastError());
    }
}

void ServerSocket::doListen()
{
	int iResult = listen(socketConnection, SOMAXCONN);
    if (iResult == SOCKET_ERROR)
    {
		Log::getInstance()->error("[SOCKET] listen() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
        throw NetworkException(WSAGetLastError());
    }
}

ClientSocket* ServerSocket::doAccept()
{
	SOCKET socket = accept(socketConnection, NULL, NULL);
	if (socket == INVALID_SOCKET)
	{
		Log::getInstance()->error("[SOCKET] accept() failed! ; (ErrorCode: " + Log::getInstance()->intToString(WSAGetLastError()) + " )");
		throw NetworkException(WSAGetLastError());
	}
	return new ClientSocket(socket);
}

void ServerSocket::setOption(OptionLevel ol, OptionName on)
{
	int level = 0;
	int name = 0;
	BOOL TRUE_BOOLEAN_VALUE = TRUE;
    int BOOLEAN_OPTION_LENGTH = sizeof(BOOL);
	if (ol == TCP)
	{
		level = IPPROTO_TCP;
	}
	if (on == NO_DELAY)
	{
		name = TCP_NODELAY;
	}
	if (level > 0 && name > 0)
	{
		if (setsockopt(socketConnection, level, name, (char*)&TRUE_BOOLEAN_VALUE, BOOLEAN_OPTION_LENGTH) == SOCKET_ERROR)
		{
			std::string message("Unable to set option ");
			message.append(getSocketOptionReadable(on)).append(" for socket level ").append(getSocketLevelReadable(ol)).append(".");
			Log::getInstance()->error("[SOCKET] " + message);
			throw NetworkException(message);
		}
	}
}

std::string ServerSocket::getSocketOptionReadable(int option)
{
	std::string readableOption = "";
	if (option == NO_DELAY)
	{
		readableOption = "TCP_NODELAY";
	}
	return readableOption;
}

std::string ServerSocket::getSocketLevelReadable(int level)
{
	std::string readableLevel = "";
	if (level == TCP)
	{
		readableLevel = "IPPROTO_TCP";
	}
	return readableLevel;
}
